package core;

import config.ConfigManager;
import assets.AssetManager;
import util.common.AbstractServer;
import filesystem.FileManager;
import graphics.GraphicsManager;
import graphics.gui.overlay.OverlayManager;
import graphics.rendering.shaders.ShaderManager;
import graphics.scene.Scene;
import hid.HIDManager;

/**
 * Represents an entire engine reference. Provides startup, shutdown of subsystems
 * and the main loop. Only one of these should be created at a time.
 * @author Joel Rausch
 */
public class Lunecy {
	
	/** Server implementation which controls engine content. */
	public AbstractServer server;
	
	/** Global file manager reference. */
	public final FileManager files;
	
	/** Global configuration manager reference. */
	public final ConfigManager config = new ConfigManager();
	
	/** Global asset manager reference. */ 
	public final AssetManager assets = new AssetManager();
	
	/** Global graphics manager reference. */
	public final GraphicsManager graphics = new GraphicsManager();
	
	/** Global shader manager reference. */
	public final ShaderManager shaders = new ShaderManager();
	
	/** Global overlay manager reference. */
	public final OverlayManager overlay = new OverlayManager();
	
	/** Global HID (human interface device) reference. */
	public final HIDManager hid = new HIDManager();
	
	/** Global scene reference. */
	public Scene scene = new Scene();
	
	/** Global exit status which is polled by main loop every frame. */
	public boolean exit;
	
	/**
	 * Constructs an engine with a server to provide content.
	 * @param name Name of the program folder
	 * @param server Server implementation
	 */
	public Lunecy(String name, AbstractServer server) {
		exit = false;
		this.server = server;
		this.files = new FileManager(name);
		
		try {
			// Bind subsystems to engine
			files.bindManager(this);
			config.bindManager(this);
			assets.bindManager(this);
			graphics.bindManager(this);
			shaders.bindManager(this);
			overlay.bindManager(this);
			hid.bindManager(this);
			server.bindManager(this);
			
			// Startup in order
			files.startup();
			config.startup();
			assets.startup();
			graphics.startup();
			shaders.startup();
			overlay.startup();
			hid.startup();
			server.startup();
		} catch(Exception e) {
			e.printStackTrace();
			exit = true;
		}
	}
	
	public void mainLoop() {
		while(!exit) {
			assets.update();
			hid.update();
			server.update();
			overlay.update();
			graphics.renderFrame();
		}
		
		// Try to exit gracefully
		try {
			shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Shutdown managers in reverse order.
	 * @throws Exception 
	 */
	private void shutdown() throws Exception {
		server.shutdown();
		hid.shutdown();
		overlay.shutdown();
		shaders.shutdown();
		graphics.shutdown();
		assets.shutdown();
		config.shutdown();
		files.shutdown();
	}
}
